﻿namespace Pathfinding
{
    using Pathfinding.Serialization;
    using System;
    using System.Collections.Generic;
    using UnityEngine;

    public class GridNode : GraphNode
    {
        private static GridGraph[] _gridGraphs = new GridGraph[0];
        public uint[] connectionCosts;
        public GraphNode[] connections;
        protected ushort gridFlags;
        private const int GridFlagsConnectionBit0 = 1;
        private const int GridFlagsConnectionMask = 0xff;
        private const int GridFlagsConnectionOffset = 0;
        private const int GridFlagsEdgeNodeMask = 0x400;
        private const int GridFlagsEdgeNodeOffset = 10;
        private const int GridFlagsWalkableErosionMask = 0x100;
        private const int GridFlagsWalkableErosionOffset = 8;
        private const int GridFlagsWalkableTmpMask = 0x200;
        private const int GridFlagsWalkableTmpOffset = 9;
        protected int nodeInGridIndex;

        public GridNode(AstarPath astar) : base(astar)
        {
        }

        public override void AddConnection(GraphNode node, uint cost)
        {
            if (this.connections != null)
            {
                for (int j = 0; j < this.connections.Length; j++)
                {
                    if (this.connections[j] == node)
                    {
                        this.connectionCosts[j] = cost;
                        return;
                    }
                }
            }
            int index = (this.connections == null) ? 0 : this.connections.Length;
            GraphNode[] nodeArray = new GraphNode[index + 1];
            uint[] numArray = new uint[index + 1];
            for (int i = 0; i < index; i++)
            {
                nodeArray[i] = this.connections[i];
                numArray[i] = this.connectionCosts[i];
            }
            nodeArray[index] = node;
            numArray[index] = cost;
            this.connections = nodeArray;
            this.connectionCosts = numArray;
        }

        public override void ClearConnections(bool alsoReverse)
        {
            if (alsoReverse)
            {
                GridGraph gridGraph = GetGridGraph(base.GraphIndex);
                for (int i = 0; i < 8; i++)
                {
                    GridNode nodeConnection = gridGraph.GetNodeConnection(this, i);
                    if (nodeConnection != null)
                    {
                        nodeConnection.SetConnectionInternal((i >= 4) ? 7 : ((i + 2) % 4), false);
                    }
                }
            }
            this.ResetConnectionsInternal();
            if (alsoReverse && (this.connections != null))
            {
                for (int j = 0; j < this.connections.Length; j++)
                {
                    this.connections[j].RemoveConnection(this);
                }
            }
            this.connections = null;
            this.connectionCosts = null;
        }

        public override void DeserializeNode(GraphSerializationContext ctx)
        {
            base.DeserializeNode(ctx);
            base.position = new Int3(ctx.reader.ReadInt32(), ctx.reader.ReadInt32(), ctx.reader.ReadInt32());
            this.gridFlags = ctx.reader.ReadUInt16();
        }

        public override void FloodFill(Stack<GraphNode> stack, uint region)
        {
            GridGraph gridGraph = GetGridGraph(base.GraphIndex);
            int[] neighbourOffsets = gridGraph.neighbourOffsets;
            GridNode[] nodes = gridGraph.nodes;
            for (int i = 0; i < 8; i++)
            {
                if (this.GetConnectionInternal(i))
                {
                    GridNode t = nodes[this.nodeInGridIndex + neighbourOffsets[i]];
                    if ((t != null) && (t.Area != region))
                    {
                        t.Area = region;
                        stack.Push(t);
                    }
                }
            }
            if (this.connections != null)
            {
                for (int j = 0; j < this.connections.Length; j++)
                {
                    GraphNode node2 = this.connections[j];
                    if (node2.Area != region)
                    {
                        node2.Area = region;
                        stack.Push(node2);
                    }
                }
            }
        }

        public bool GetConnectionInternal(int dir)
        {
            return (((this.gridFlags >> dir) & 1) != 0);
        }

        public override void GetConnections(GraphNodeDelegate del)
        {
            GridGraph gridGraph = GetGridGraph(base.GraphIndex);
            int[] neighbourOffsets = gridGraph.neighbourOffsets;
            GridNode[] nodes = gridGraph.nodes;
            for (int i = 0; i < 8; i++)
            {
                if (this.GetConnectionInternal(i))
                {
                    GridNode node = nodes[this.nodeInGridIndex + neighbourOffsets[i]];
                    if (node != null)
                    {
                        del(node);
                    }
                }
            }
            if (this.connections != null)
            {
                for (int j = 0; j < this.connections.Length; j++)
                {
                    del(this.connections[j]);
                }
            }
        }

        public static GridGraph GetGridGraph(uint graphIndex)
        {
            return _gridGraphs[graphIndex];
        }

        [Obsolete("This method has been deprecated. Please use NodeInGridIndex instead.", true)]
        public int GetIndex()
        {
            return 0;
        }

        public override bool GetPortal(GraphNode other, List<Vector3> left, List<Vector3> right, bool backwards)
        {
            if (backwards)
            {
                return true;
            }
            GridGraph gridGraph = GetGridGraph(base.GraphIndex);
            int[] neighbourOffsets = gridGraph.neighbourOffsets;
            GridNode[] nodes = gridGraph.nodes;
            for (int i = 0; i < 4; i++)
            {
                if (this.GetConnectionInternal(i) && (other == nodes[this.nodeInGridIndex + neighbourOffsets[i]]))
                {
                    Vector3 vector = (Vector3) (((Vector3) (base.position + other.position)) * 0.5f);
                    Vector3 vector2 = Vector3.Cross(gridGraph.collision.up, (Vector3) (other.position - base.position));
                    vector2.Normalize();
                    vector2 = (Vector3) (vector2 * (gridGraph.nodeSize * 0.5f));
                    left.Add(vector - vector2);
                    right.Add(vector + vector2);
                    return true;
                }
            }
            for (int j = 4; j < 8; j++)
            {
                if (this.GetConnectionInternal(j) && (other == nodes[this.nodeInGridIndex + neighbourOffsets[j]]))
                {
                    bool flag = false;
                    bool flag2 = false;
                    if (this.GetConnectionInternal(j - 4))
                    {
                        GridNode node = nodes[this.nodeInGridIndex + neighbourOffsets[j - 4]];
                        if (node.Walkable && node.GetConnectionInternal(((j - 4) + 1) % 4))
                        {
                            flag = true;
                        }
                    }
                    if (this.GetConnectionInternal(((j - 4) + 1) % 4))
                    {
                        GridNode node2 = nodes[this.nodeInGridIndex + neighbourOffsets[((j - 4) + 1) % 4]];
                        if (node2.Walkable && node2.GetConnectionInternal(j - 4))
                        {
                            flag2 = true;
                        }
                    }
                    Vector3 vector3 = (Vector3) (((Vector3) (base.position + other.position)) * 0.5f);
                    Vector3 vector4 = Vector3.Cross(gridGraph.collision.up, (Vector3) (other.position - base.position));
                    vector4.Normalize();
                    vector4 = (Vector3) (vector4 * (gridGraph.nodeSize * 1.4142f));
                    left.Add(vector3 - (!flag2 ? Vector3.zero : vector4));
                    right.Add(vector3 + (!flag ? Vector3.zero : vector4));
                    return true;
                }
            }
            return false;
        }

        public override void Open(Path path, PathNode pathNode, PathHandler handler)
        {
            GridGraph gridGraph = GetGridGraph(base.GraphIndex);
            int[] neighbourOffsets = gridGraph.neighbourOffsets;
            uint[] neighbourCosts = gridGraph.neighbourCosts;
            GridNode[] nodes = gridGraph.nodes;
            ushort pathID = handler.PathID;
            for (int i = 0; i < 8; i++)
            {
                if (this.GetConnectionInternal(i))
                {
                    GridNode node = nodes[this.nodeInGridIndex + neighbourOffsets[i]];
                    if (path.CanTraverse(node))
                    {
                        PathNode node2 = handler.GetPathNode(node);
                        if (node2.pathID != pathID)
                        {
                            node2.parent = pathNode;
                            node2.pathID = pathID;
                            node2.cost = neighbourCosts[i];
                            node2.H = path.CalculateHScore(node);
                            node.UpdateG(path, node2);
                            handler.PushNode(node2);
                        }
                        else
                        {
                            uint num3 = neighbourCosts[i];
                            if (((pathNode.G + num3) + path.GetTraversalCost(node)) < node2.G)
                            {
                                node2.cost = num3;
                                node2.parent = pathNode;
                                node.UpdateRecursiveG(path, node2, handler);
                            }
                            else if (((node2.G + num3) + path.GetTraversalCost(this)) < pathNode.G)
                            {
                                pathNode.parent = node2;
                                pathNode.cost = num3;
                                this.UpdateRecursiveG(path, pathNode, handler);
                            }
                        }
                    }
                }
            }
            if (this.connections != null)
            {
                for (int j = 0; j < this.connections.Length; j++)
                {
                    GraphNode node3 = this.connections[j];
                    if (path.CanTraverse(node3))
                    {
                        PathNode node4 = handler.GetPathNode(node3);
                        if (node4.pathID != pathID)
                        {
                            node4.parent = pathNode;
                            node4.pathID = pathID;
                            node4.cost = this.connectionCosts[j];
                            node4.H = path.CalculateHScore(node3);
                            node3.UpdateG(path, node4);
                            handler.PushNode(node4);
                        }
                        else
                        {
                            uint num5 = this.connectionCosts[j];
                            if (((pathNode.G + num5) + path.GetTraversalCost(node3)) < node4.G)
                            {
                                node4.cost = num5;
                                node4.parent = pathNode;
                                node3.UpdateRecursiveG(path, node4, handler);
                            }
                            else if ((((node4.G + num5) + path.GetTraversalCost(this)) < pathNode.G) && node3.ContainsConnection(this))
                            {
                                pathNode.parent = node4;
                                pathNode.cost = num5;
                                this.UpdateRecursiveG(path, pathNode, handler);
                            }
                        }
                    }
                }
            }
        }

        public override void RemoveConnection(GraphNode node)
        {
            if (this.connections != null)
            {
                for (int i = 0; i < this.connections.Length; i++)
                {
                    if (this.connections[i] == node)
                    {
                        int length = this.connections.Length;
                        GraphNode[] nodeArray = new GraphNode[length - 1];
                        uint[] numArray = new uint[length - 1];
                        for (int j = 0; j < i; j++)
                        {
                            nodeArray[j] = this.connections[j];
                            numArray[j] = this.connectionCosts[j];
                        }
                        for (int k = i + 1; k < length; k++)
                        {
                            nodeArray[k - 1] = this.connections[k];
                            numArray[k - 1] = this.connectionCosts[k];
                        }
                        this.connections = nodeArray;
                        this.connectionCosts = numArray;
                        return;
                    }
                }
            }
        }

        public void ResetConnectionsInternal()
        {
            this.gridFlags = (ushort) (this.gridFlags & -256);
        }

        public override void SerializeNode(GraphSerializationContext ctx)
        {
            base.SerializeNode(ctx);
            ctx.writer.Write(this.position.x);
            ctx.writer.Write(this.position.y);
            ctx.writer.Write(this.position.z);
            ctx.writer.Write(this.gridFlags);
        }

        public void SetConnectionInternal(int dir, bool value)
        {
            this.gridFlags = (ushort) ((this.gridFlags & ~(((int) 1) << dir)) | (((value == null) ? 0 : 1) << dir));
        }

        public static void SetGridGraph(int graphIndex, GridGraph graph)
        {
            if (_gridGraphs.Length <= graphIndex)
            {
                GridGraph[] graphArray = new GridGraph[graphIndex + 1];
                for (int i = 0; i < _gridGraphs.Length; i++)
                {
                    graphArray[i] = _gridGraphs[i];
                }
                _gridGraphs = graphArray;
            }
            _gridGraphs[graphIndex] = graph;
        }

        public override void UpdateRecursiveG(Path path, PathNode pathNode, PathHandler handler)
        {
            GridGraph gridGraph = GetGridGraph(base.GraphIndex);
            int[] neighbourOffsets = gridGraph.neighbourOffsets;
            GridNode[] nodes = gridGraph.nodes;
            base.UpdateG(path, pathNode);
            handler.PushNode(pathNode);
            ushort pathID = handler.PathID;
            for (int i = 0; i < 8; i++)
            {
                if (this.GetConnectionInternal(i))
                {
                    GridNode node = nodes[this.nodeInGridIndex + neighbourOffsets[i]];
                    PathNode node2 = handler.GetPathNode(node);
                    if ((node2.parent == pathNode) && (node2.pathID == pathID))
                    {
                        node.UpdateRecursiveG(path, node2, handler);
                    }
                }
            }
            if (this.connections != null)
            {
                for (int j = 0; j < this.connections.Length; j++)
                {
                    GraphNode node3 = this.connections[j];
                    PathNode node4 = handler.GetPathNode(node3);
                    if ((node4.parent == pathNode) && (node4.pathID == pathID))
                    {
                        node3.UpdateRecursiveG(path, node4, handler);
                    }
                }
            }
        }

        public bool EdgeNode
        {
            get
            {
                return ((this.gridFlags & 0x400) != 0);
            }
            set
            {
                this.gridFlags = (ushort) ((this.gridFlags & -1025) | (!value ? 0 : 0x400));
            }
        }

        public int NodeInGridIndex
        {
            get
            {
                return this.nodeInGridIndex;
            }
            set
            {
                this.nodeInGridIndex = value;
            }
        }

        public bool TmpWalkable
        {
            get
            {
                return ((this.gridFlags & 0x200) != 0);
            }
            set
            {
                this.gridFlags = (ushort) ((this.gridFlags & -513) | (!value ? 0 : 0x200));
            }
        }

        public bool WalkableErosion
        {
            get
            {
                return ((this.gridFlags & 0x100) != 0);
            }
            set
            {
                this.gridFlags = (ushort) ((this.gridFlags & -257) | (!value ? 0 : 0x100));
            }
        }
    }
}

